home *** CD-ROM | disk | FTP | other *** search
- // DocControl.m
- //
- // Free software created 1 Feb 1992
- // by Paul Burchard <burchard@math.utah.edu>.
-
- #import "DocControl.h"
- #import "Doc.h"
- #import <appkit/appkit.h>
- #import <sys/file.h>
- #import <sys/param.h> /* for MAXPATHLEN */
- #import <string.h>
-
-
- @implementation DocControl
-
- - (int)computeTypeIndexOf:(const char *)fileName inList:(const char **)theTypes
- {
- int hNum;
- const char **aType, *thisType;
-
- if(!fileName || !theTypes) return (-1);
- if(!(thisType = strrchr(fileName, '/'))) thisType = fileName;
- if(thisType = strrchr(thisType, '.')) thisType++;
- else thisType = "";
- for(hNum=0, aType=theTypes; *aType; hNum++, aType++)
- if(0 == strcmp(*aType, thisType)) break;
- if(!*aType) return (-1);
- return hNum;
- }
-
- - (int)getTypeAndOpenPath:(char *)fileBuf
- defaultFolder:(const char *)theFolder types:(const char **)theTypes
- {
- int hNum;
- const char *const *files;
- static id openPanel = nil;
-
- if(!fileBuf || !theTypes) return (-1);
- if(!openPanel) openPanel = [OpenPanel new];
- [NXApp setAutoupdate:NO];
- if(theFolder && *theFolder)
- {
- if(![openPanel runModalForDirectory:theFolder
- file:NULL types:theTypes])
- { [NXApp setAutoupdate:YES]; return (-1); }
- }
- else
- {
- if(![openPanel runModalForTypes:theTypes])
- { [NXApp setAutoupdate:YES]; return (-1); }
- }
- files = [openPanel filenames];
- if(!files || !files[0])
- { [NXApp setAutoupdate:YES]; return (-1); }
- strcpy(fileBuf, files[0]);
- [NXApp setAutoupdate:YES];
- if((hNum=[self computeTypeIndexOf:fileBuf inList:theTypes]) < 0)
- return (-1);
- return hNum;
- }
-
- // defaultPath: overrides defaultFolder: arg
- - (BOOL)getSavePath:(char *)fileBuf
- defaultFolder:(const char *)theFolder defaultPath:(const char *)origName
- type:(const char *)theType
- {
- static id savePanel = nil;
- BOOL ok, as;
- char dirName[MAXPATHLEN+1], fileName[MAXPATHLEN+1];
-
- if(!savePanel)
- {
- savePanel = [SavePanel new];
- [savePanel setDirectory:[[self class] defaultFolder]];
- }
- if(theType && *theType) [savePanel setRequiredFileType:theType];
- as = NO;
- if(origName && *origName)
- {
- char *p;
- as = YES;
- strcpy(dirName, origName);
- if(!(p=strrchr(dirName, '/'))) as = NO;
- else if(p!=dirName) *p = 0;
- if(p = strrchr(origName, '/')) strcpy(fileName, p+1);
- else strcpy(fileName, origName);
- if(!fileName[0]) as = NO;
- }
- if(!as && theFolder && *theFolder) [savePanel setDirectory:theFolder];
-
- [NXApp setAutoupdate:NO];
- if(as) ok = [savePanel runModalForDirectory:dirName file:fileName];
- else ok = [savePanel runModal];
- if(!ok) { [NXApp setAutoupdate:YES]; return NO; }
- strcpy(fileBuf,[savePanel filename]);
- [NXApp setAutoupdate:YES];
- return YES;
- }
-
- - init
- {
- [super init];
- convertWindowToDoc = [[HashTable alloc] initKeyDesc:"@" valueDesc:"@"];
- return self;
- }
-
- - setDocHandlers:HandlerList
- {
- int i, n;
-
- if((n=[HandlerList count]) <= 0) return nil;
- if(fileTypes) NX_FREE(fileTypes);
- NX_MALLOC(fileTypes, const char *, n+1);
- if(!fileTypes) return nil;
- for(i=0; i<n; i++) fileTypes[i] = [[HandlerList objectAt:i] fileType];
- fileTypes[i] = 0;
- [DocHandlers free];
- DocHandlers = HandlerList;
- return self;
- }
-
- + (const char *)defaultFolder
- {
- return NXHomeDirectory();
- }
-
- - appDidInit:sender
- {
- if(launchWithCreateDoc) [self createDoc:self];
- return self;
- }
-
- - free
- {
- [convertWindowToDoc free];
- if(fileTypes) NX_FREE(fileTypes);
- [DocHandlers free];
- return [super free];
- }
-
- - (BOOL)appAcceptsAnotherFile:sender
- {
- return YES;
- }
-
- - (int)app:sender openFile:(const char *)fileName type:(const char *)aType
- {
- int hNum, nHandlers = [DocHandlers count];
-
- if(!fileName || !aType || [DocHandlers count]<=0) return NO;
- launchWithCreateDoc = NO;
- for(hNum=0; hNum<nHandlers; hNum++)
- if(0 == strcmp(aType, [[DocHandlers objectAt:hNum] fileType]))
- break;
- if(hNum >= nHandlers) return NO;
- if([self openForHandlerAt:hNum name:fileName]) return YES;
- else return NO;
- }
-
- - setMainDoc:aDoc
- {
- [[aDoc window] makeKeyAndOrderFront:nil];
- return self;
- }
-
- - mainDoc
- {
- if(![NXApp mainWindow]) return nil;
- return (id)[convertWindowToDoc valueForKey:(void *)[NXApp mainWindow]];
- }
-
- - stringTable
- {
- return stringTable;
- }
-
- - createDocForHandlerAt:(int)hNum
- {
- int useNum;
- id theDoc;
-
- if([DocHandlers count] <= 0) return nil;
- if(hNum<0 || hNum>=[DocHandlers count]) useNum = 0;
- else useNum = hNum;
- theDoc = [[[DocHandlers objectAt:useNum] alloc] init];
- if(!theDoc || ![theDoc window]) { [theDoc free]; return nil; }
- [self setMainDoc:theDoc];
- [convertWindowToDoc
- insertKey:(void *)[theDoc window] value:(void *)theDoc];
- [[theDoc window] setDocEdited:NO];
- return theDoc;
- }
-
- - createDoc:sender
- {
- return [self createDocForHandlerAt:(-1)];
- }
-
- - createDoc1:sender
- {
- return [self createDocForHandlerAt:0];
- }
-
- - createDoc2:sender
- {
- return [self createDocForHandlerAt:1];
- }
-
- - createDoc3:sender
- {
- return [self createDocForHandlerAt:2];
- }
-
- - handlerForFile:(const char *)fileName
- {
- int hNum;
-
- if(!fileName || [DocHandlers count]<=0 || !fileTypes) return nil;
- hNum = [self computeTypeIndexOf:fileName inList:fileTypes];
- if(hNum<0 || hNum>=[DocHandlers count]) return nil;
- return [DocHandlers objectAt:hNum];
- }
-
- - openForHandlerAt:(int)hNum name:(const char *)fileName
- {
- int gotNum = (-1);
- id theDoc, TheHandler;
- char fileBuf[MAXPATHLEN+1];
- const char *oneType[2] = {0, 0};
-
- if([DocHandlers count]<=0 || !fileTypes) return nil;
-
- // Get Doc handler and file name (putting latter into fileBuf).
- if(fileName)
- {
- strcpy(fileBuf, fileName);
- if(hNum>=0 && hNum<[DocHandlers count]) gotNum = hNum;
- else gotNum = [self computeTypeIndexOf:fileName inList:fileTypes];
- }
- else if(hNum>=0 && hNum<[DocHandlers count])
- {
- oneType[0] = fileTypes[hNum];
- gotNum = [self getTypeAndOpenPath:fileBuf
- defaultFolder:[[DocHandlers objectAt:hNum] defaultFolder]
- types:oneType];
- }
- else gotNum = [self getTypeAndOpenPath:fileBuf
- defaultFolder:[[self class] defaultFolder]
- types:fileTypes];
- if(gotNum<0 || gotNum>=[DocHandlers count]) return nil;
- TheHandler = [DocHandlers objectAt:gotNum];
-
- // Check if this file is already open in some Doc; if so just pop up.
- if(theDoc = [TheHandler docForFileName:fileBuf])
- { [self setMainDoc:theDoc]; return theDoc; }
-
- // Create new Doc for this file, load it in, and pop it up.
- theDoc = [[TheHandler alloc] init];
- [theDoc setFileName:fileBuf];
- if(!theDoc || ![theDoc window] || ![theDoc load:self])
- {
- NXRunAlertPanel([stringTable valueForStringKey:"Open"],
- [stringTable valueForStringKey:"Cannot read %s!"],
- [stringTable valueForStringKey:"OK"], NULL, NULL, fileBuf);
- [theDoc free]; return nil;
- }
- [self setMainDoc:theDoc];
- [convertWindowToDoc
- insertKey:(void *)[theDoc window] value:(void *)theDoc];
- [[theDoc window] setDocEdited:NO];
- return theDoc;
- }
-
- - open:sender
- {
- return [self openForHandlerAt:(-1) name:NULL];
- }
-
- - open1:sender
- {
- return [self openForHandlerAt:0 name:NULL];
- }
-
- - open2:sender
- {
- return [self openForHandlerAt:1 name:NULL];
- }
-
- - open3:sender
- {
- return [self openForHandlerAt:2 name:NULL];
- }
-
- - saveDoc:theDoc as:(BOOL)yn
- {
- int choice;
- id otherDoc;
- const char *fileName;
- char fileBuf[MAXPATHLEN+1];
-
- // Get new file name if still untitled or "save as" is true.
- if(yn || ![theDoc fileName])
- {
- if(![self getSavePath:fileBuf
- defaultFolder:[[theDoc class] defaultFolder]
- defaultPath:[theDoc fileName]
- type:[[theDoc class] fileType]])
- return nil;
- // Make sure new file name is not already in use in this app.
- otherDoc = [[theDoc class] docForFileName:fileBuf];
- if(otherDoc && otherDoc!=theDoc)
- {
- /*!!!
- choice = NXRunAlertPanel([stringTable valueForStringKey:"Save"],
- [stringTable valueForStringKey:"You already have %s open in another window.\nDestroy other window?"],
- [stringTable valueForStringKey:"Yes"],
- [stringTable valueForStringKey:"Cancel"], NULL, fileBuf);
- if(choice != NX_ALERTDEFAULT) return nil;
- !!!*/
- [[otherDoc window] setDocEdited:NO];
- [self closeDoc:otherDoc andFree:YES];
- }
- [theDoc setFileName:fileBuf];
- }
- if(!(fileName = [theDoc fileName])) return nil;
-
- // If backup option on, append "~" to the existing file.
- if([[theDoc class] backupOnSave] && access(fileName, F_OK)==0)
- {
- strcpy(fileBuf, fileName);
- strcat(fileBuf, "~");
- rename(fileName, fileBuf);
- }
-
- // Write to file and alert user on error.
- if(![theDoc dump:self])
- {
- NXRunAlertPanel([stringTable valueForStringKey:"Save"],
- [stringTable valueForStringKey:"Cannot write %s!"],
- [stringTable valueForStringKey:"OK"], NULL, NULL, fileName);
- return nil;
- }
-
- [[theDoc window] setDocEdited:NO];
- return theDoc;
- }
-
- - save:sender
- {
- return [self saveDoc:[self mainDoc] as:NO];
- }
-
- - saveAs:sender
- {
- return [self saveDoc:[self mainDoc] as:YES];
- }
-
- - revertDocToSaved:theDoc
- {
- const char *fileName;
-
- // If still untitled or unedited no reversion is possible.
- if([theDoc window] && ![[theDoc window] isDocEdited]) return nil;
- if(!(fileName = [theDoc fileName])) return nil;
-
- // Ask user if reversion is really what was meant.
- if(NXRunAlertPanel([stringTable valueForStringKey:"Revert"],
- [stringTable valueForStringKey:"Revert to saved version of %s?"],
- [stringTable valueForStringKey:"Revert"],
- [stringTable valueForStringKey:"Cancel"], NULL, fileName)
- != NX_ALERTDEFAULT)
- return nil;
-
- // Re-read file from disk.
- if(![theDoc load:self])
- {
- NXRunAlertPanel([stringTable valueForStringKey:"Revert"],
- [stringTable valueForStringKey:"Cannot read %s!"],
- [stringTable valueForStringKey:"OK"], NULL, NULL, fileName);
- return nil;
- }
- [[theDoc window] setDocEdited:NO];
- return theDoc;
- }
-
- - revertToSaved:sender
- {
- return [self revertDocToSaved:[self mainDoc]];
- }
-
- - closeDoc:theDoc andFree:(BOOL)yn
- {
- int choice;
- const char *fileName;
-
- // Untitled files OK---user may still want to save them.
- fileName = [theDoc fileName];
-
- // Give user a chance to save if edited.
- if([[theDoc window] isDocEdited])
- {
- choice = NXRunAlertPanel([stringTable valueForStringKey:"Close"],
- [stringTable valueForStringKey:"%s has been modified.\nSave it?"],
- [stringTable valueForStringKey:"Yes"],
- [stringTable valueForStringKey:"No"],
- [stringTable valueForStringKey:"Cancel"],
- fileName ? fileName : [stringTable valueForStringKey:"This file"]);
- switch (choice)
- {
- case NX_ALERTALTERNATE:
- break;
- case NX_ALERTOTHER:
- return nil;
- case NX_ALERTDEFAULT:
- default:
- [self saveDoc:theDoc as:(fileName ? NO : YES)];
- break;
- }
- }
- [[theDoc window] setDocEdited:NO];
-
- // Remove Doc from index.
- [convertWindowToDoc removeKey:(void *)[theDoc window]];
-
- // Note that doc closes its window upon freeing.
- if(yn) [[theDoc window] close];//!!!
- [theDoc setFileName:NULL];//!!!
- //!!!if(yn) [theDoc free];
- return self;
- }
-
- - close:sender
- {
- return [self closeDoc:[self mainDoc] andFree:YES];
- }
-
- - appWillTerminate:sender
- {
- const void *windowKey;
- void *docValue;
- NXHashState state = [convertWindowToDoc initState];
-
- // Give user a chance to review unsaved documents and cancel the Quit.
- while([convertWindowToDoc nextState:&state key:&windowKey value:&docValue])
- if([(id)windowKey isDocEdited])
- {
- [self setMainDoc:(id)docValue];
- if(![self closeDoc:(id)docValue andFree:YES]) return nil;
- }
- return self;
- }
-
- @end
-